|
1
|
|
|
import React from 'react'; |
|
2
|
|
|
import PropTypes from 'prop-types'; |
|
3
|
|
|
import ReactDOM from 'react-dom'; |
|
4
|
|
|
|
|
5
|
|
|
import { |
|
6
|
|
|
Row, |
|
7
|
|
|
Col, |
|
8
|
|
|
FormControl, |
|
9
|
|
|
ButtonToolbar, |
|
10
|
|
|
ButtonGroup, |
|
11
|
|
|
Button, |
|
12
|
|
|
OverlayTrigger, |
|
13
|
|
|
Tooltip, |
|
14
|
|
|
} from 'react-bootstrap'; |
|
15
|
|
|
|
|
16
|
|
|
const defaultCondition = { comparator: '*' }; |
|
17
|
|
|
const defaultConditionAdded = { comparator: '~' }; |
|
18
|
|
|
|
|
19
|
|
|
export default class VersionSelector extends React.Component { |
|
20
|
|
|
constructor(props) { |
|
21
|
|
|
super(props); |
|
22
|
|
|
this.state = { |
|
23
|
|
|
values: [defaultCondition], |
|
24
|
|
|
addButtonDisabled: false, |
|
25
|
|
|
}; |
|
26
|
|
|
this.focusInputs = {}; |
|
27
|
|
|
} |
|
28
|
|
|
|
|
29
|
|
|
componentWillReceiveProps(newProps) { |
|
30
|
|
|
if (newProps.values) { |
|
31
|
|
|
this.setState({ |
|
32
|
|
|
values: newProps.values, |
|
33
|
|
|
addButtonDisabled: newProps.values.some(val => val.comparator === '*'), |
|
34
|
|
|
}); |
|
35
|
|
|
} |
|
36
|
|
|
} |
|
37
|
|
|
|
|
38
|
|
|
onVersionChanged(index, changedItem) { |
|
39
|
|
|
const newValues = this.state.values.slice(); |
|
40
|
|
|
newValues[index] = Object.assign({}, this.state.values[index], changedItem); |
|
41
|
|
|
this.setState({ |
|
42
|
|
|
values: newValues, |
|
43
|
|
|
addButtonDisabled: newValues.some(val => val.comparator === '*'), |
|
44
|
|
|
}, () => { |
|
45
|
|
|
if (changedItem.comparator && this.focusInputs[index][changedItem.comparator]) { |
|
46
|
|
|
ReactDOM.findDOMNode(this.focusInputs[index][changedItem.comparator]).focus(); |
|
47
|
|
|
} |
|
48
|
|
|
this.onChanged(); |
|
49
|
|
|
}); |
|
50
|
|
|
} |
|
51
|
|
|
|
|
52
|
|
|
onChanged() { |
|
53
|
|
|
if (typeof this.props.onChange === 'function') { |
|
54
|
|
|
this.props.onChange(this.state.values); |
|
55
|
|
|
} |
|
56
|
|
|
} |
|
57
|
|
|
|
|
58
|
|
|
addCondition() { |
|
59
|
|
|
const newValues = this.state.values.slice(); |
|
60
|
|
|
newValues.push(defaultConditionAdded); |
|
61
|
|
|
this.setState({ values: newValues }, () => this.onChanged()); |
|
62
|
|
|
} |
|
63
|
|
|
|
|
64
|
|
|
removeCondition(idx) { |
|
65
|
|
|
const newValues = this.state.values.slice(); |
|
66
|
|
|
newValues.splice(idx, 1); |
|
67
|
|
|
this.setState({ values: newValues }, () => this.onChanged()); |
|
68
|
|
|
} |
|
69
|
|
|
|
|
70
|
|
|
render() { |
|
71
|
|
|
const values = this.state.values || [defaultCondition]; // default |
|
72
|
|
|
return ( |
|
73
|
|
|
<div className="version-selector-container"> |
|
74
|
|
|
{values.map((value, index) => { |
|
75
|
|
|
return ( |
|
76
|
|
|
<div className="selector-item" key={index}> |
|
77
|
|
|
<ButtonToolbar className="selector-comparator"> |
|
78
|
|
|
<ButtonGroup> |
|
79
|
|
|
<OverlayTrigger placement="top" overlay={<Tooltip>모든 범위</Tooltip>}> |
|
80
|
|
|
<Button |
|
81
|
|
|
active={value.comparator === '*'} |
|
82
|
|
|
onClick={() => this.onVersionChanged(index, { comparator: '*' })} |
|
83
|
|
|
disabled={this.props.disabled} |
|
84
|
|
|
> |
|
85
|
|
|
✱ {/* asterisk */} |
|
86
|
|
|
</Button> |
|
87
|
|
|
</OverlayTrigger> |
|
88
|
|
|
<OverlayTrigger placement="top" overlay={<Tooltip>범위 지정</Tooltip>}> |
|
89
|
|
|
<Button |
|
90
|
|
|
active={value.comparator === '~'} |
|
91
|
|
|
onClick={() => this.onVersionChanged(index, { comparator: '~' })} |
|
92
|
|
|
disabled={this.props.disabled} |
|
93
|
|
|
> |
|
94
|
|
|
∼ {/* tilda */} |
|
95
|
|
|
</Button> |
|
96
|
|
|
</OverlayTrigger> |
|
97
|
|
|
<OverlayTrigger placement="top" overlay={<Tooltip>일치</Tooltip>}> |
|
98
|
|
|
<Button |
|
99
|
|
|
active={value.comparator === '='} |
|
100
|
|
|
onClick={() => this.onVersionChanged(index, { comparator: '=' })} |
|
101
|
|
|
disabled={this.props.disabled} style={{ paddingTop: '5px', paddingBottom: '7px' }} |
|
102
|
|
|
> |
|
103
|
|
|
= |
|
104
|
|
|
</Button> |
|
105
|
|
|
</OverlayTrigger> |
|
106
|
|
|
</ButtonGroup> |
|
107
|
|
|
</ButtonToolbar> |
|
108
|
|
|
<FormControl |
|
109
|
|
|
className="selector-inputs" |
|
110
|
|
|
type="text" |
|
111
|
|
|
value={'모든 버전 대상'} |
|
112
|
|
|
style={value.comparator !== '*' ? { display: 'none' } : {}} |
|
113
|
|
|
readOnly |
|
114
|
|
|
/> |
|
115
|
|
|
<FormControl |
|
116
|
|
|
ref={(f) => { |
|
117
|
|
|
if (!this.focusInputs[index]) { |
|
118
|
|
|
this.focusInputs[index] = {}; |
|
119
|
|
|
} |
|
120
|
|
|
this.focusInputs[index]['~'] = f; |
|
121
|
|
|
}} |
|
122
|
|
|
className="selector-inputs" |
|
123
|
|
|
type="text" |
|
124
|
|
|
value={value.versionStart} |
|
125
|
|
|
onChange={e => this.onVersionChanged(index, { versionStart: e.target.value })} |
|
126
|
|
|
placeholder=">= 시작 버전 (X.Y.Z 형태)" |
|
127
|
|
|
disabled={this.props.disabled} |
|
128
|
|
|
style={value.comparator !== '~' ? { display: 'none' } : {}} |
|
129
|
|
|
/> |
|
130
|
|
|
<FormControl |
|
131
|
|
|
className="selector-inputs selector-inputs-second" |
|
132
|
|
|
type="text" |
|
133
|
|
|
value={value.versionEnd} |
|
134
|
|
|
onChange={e => this.onVersionChanged(index, { versionEnd: e.target.value })} |
|
135
|
|
|
placeholder="< 끝 버전 (X.Y.Z 형태)" |
|
136
|
|
|
disabled={this.props.disabled} |
|
137
|
|
|
style={value.comparator !== '~' ? { display: 'none' } : {}} |
|
138
|
|
|
/> |
|
139
|
|
|
<FormControl |
|
140
|
|
|
ref={(f) => { |
|
141
|
|
|
if (!this.focusInputs[index]) { |
|
142
|
|
|
this.focusInputs[index] = {}; |
|
143
|
|
|
} |
|
144
|
|
|
this.focusInputs[index]['='] = f; |
|
145
|
|
|
}} |
|
146
|
|
|
className="selector-inputs" |
|
147
|
|
|
type="text" |
|
148
|
|
|
value={value.version} |
|
149
|
|
|
onChange={e => this.onVersionChanged(index, { version: e.target.value })} |
|
150
|
|
|
placeholder="= 일치하는 버전 (X.Y.Z 형태)" |
|
151
|
|
|
style={value.comparator !== '=' ? { display: 'none' } : {}} |
|
152
|
|
|
disabled={this.props.disabled} |
|
153
|
|
|
/> |
|
154
|
|
|
<Button |
|
155
|
|
|
className="selector-remove-btn" |
|
156
|
|
|
onClick={() => this.removeCondition(index)} |
|
157
|
|
|
disabled={index === 0 || this.props.disabled} |
|
158
|
|
|
> |
|
159
|
|
|
✕ |
|
160
|
|
|
</Button> |
|
161
|
|
|
</div> |
|
162
|
|
|
); |
|
163
|
|
|
})} |
|
164
|
|
|
<Row> |
|
165
|
|
|
<Col xs={12}> |
|
166
|
|
|
<Button |
|
167
|
|
|
className="selector-add-btn" |
|
168
|
|
|
onClick={() => this.addCondition()} |
|
169
|
|
|
disabled={this.props.disabled || this.state.addButtonDisabled} |
|
170
|
|
|
> |
|
171
|
|
|
+ 조건 추가 (OR) |
|
172
|
|
|
</Button> |
|
173
|
|
|
</Col> |
|
174
|
|
|
</Row> |
|
175
|
|
|
</div> |
|
176
|
|
|
); |
|
177
|
|
|
} |
|
178
|
|
|
} |
|
179
|
|
|
|
|
180
|
|
|
VersionSelector.propTypes = { |
|
181
|
|
|
values: PropTypes.arrayOf(PropTypes.shape({ |
|
182
|
|
|
comparator: PropTypes.string.isRequired, |
|
183
|
|
|
versionStart: PropTypes.string, |
|
184
|
|
|
versionEnd: PropTypes.string, |
|
185
|
|
|
version: PropTypes.string, |
|
186
|
|
|
})), |
|
187
|
|
|
onChange: PropTypes.func, |
|
188
|
|
|
disabled: PropTypes.bool, |
|
189
|
|
|
}; |
|
190
|
|
|
|